﻿/// <reference path="../jquery-vsdoc.js" />
/// <reference path="jquery.poshytip.js" />

//#region 为原生的DOM元素添加统一的辅助方法用于获取或设置值
HTMLElement.prototype.val = function (_newValue) {
    if (ObjHelper.IsEmpty(_newValue)) {
        console.log(this.nodeType);
        switch (this.nodeType) {
            
        }
        $(this).val(_newValue);
        return this;
    }
    else {
        return $(this).val();
    }
};

HTMLElement.prototype.ID = function (_newId) {
    if (ObjHelper.IsEmpty(_newId)) {
        return $(this).ID();
    }
    else {
        $(this).ID(_newId);
        return this;
    }
};

HTMLElement.prototype.attr = function (_key, _value) {
    if (ObjHelper.IsEmpty(_value)) {
        return $(this).attr(_key);
    }
    else {
        $(this).attr(_key, _value);
        return this;
    }
}

HTMLElement.prototype.addClass = function (_style) {
    $(this).addClass(_style);
    return this;
};

HTMLElement.prototype.removeClass = function (_style) {
    $(this).removeClass(_style);
    return this;
};

HTMLElement.prototype.reset = function () {
    this.val("");
    return this;
}
//#endregion

//#region 为jquery对象扩展一些方法，以保持与LiberUI控件对象的操作一致性
; (function ($) {
    //针对JQuery选择DOM元素后的扩展操作功能
    $.fn.extend({
        "ID": function (_newID) {
            /// <summary>
            /// 为JQuery对象提供与LiberUI控件一致的获取ID属性的方法
            /// </summary>
            /// <param name="_newID"></param>
            if (ObjHelper.IsEmpty(_newID)) {
                var cid = $(this).attr('id');
                if (!cid) {
                    cid = 'id' + Math.random();
                    $(this).attr('id', cid);
                }
                return cid;
            }
            else {
                $(this).attr('id', _newID);
                return $(this);
            }
        },

        "dispose": function () {
            /// <summary>
            /// 为JQuery对象提供与LiberUI控件一致的销毁自身的方法
            /// </summary>
            $.each(this, function () {
                $(this).remove('', false);
                delete this;
            });
        },

        "dom":function(_elm){
            if(ObjHelper.IsElement(_elm)){
                $(this).appendTo(_elm);
                return this;
            }
            else{
                return $(this);
            }
        },

        "reset":function(){
            return $(this).val("");
        },

        "Focusble":function(){
        	/// <summary>
        	/// 判断一个元素是否可设置焦点
            /// </summary>
            return $(this).attr('tabindex') !== undefined;
        },

        "Mask": function (_cnt) {
            /// <summary>
            /// 创建一个简单的遮罩层覆盖在指定元素之上
            /// </summary>
            var elm = $(this);
            var m = $("<div class='MaskLayer'></div>").attr("MaskID", elm.ID());
            LayoutHelper.Layout2Parent(m, "Cover", null, true);
            m.prependTo(elm);
            if (ObjHelper.IsElement(_cnt)) {
                m.append(_cnt);
                LayoutHelper.Layout2Parent(_cnt, "AllCenter", null);
            }
            return m;
        },

        "Unmask": function () {
        	/// <summary>
        	/// 移除通过Mask方法创建的遮罩层
        	/// </summary>
        	/// <returns type=""></returns>
            var id = $(this).ID();
            $("[MaskID='" + id + "']").remove();
            return this;
        }
    })
})(jQuery);

var ObjHelper = {
    IsEmpty: function (_obj) {
        /// <summary>
        /// 判定一个对象是否为空
        /// </summary>
        /// <param name="_obj">字符串、DOM、数组、JQuery对象</param>
        if (_obj == undefined || _obj == null) {
            return true;
        }
        else if (typeof _obj == "string") {
            return $.trim(_obj).length == 0;
        }
        else if (Array.isArray(_obj)) {
            return _obj.length == 0;
        }
        else if (_obj instanceof jQuery) {
            return _obj.length == 0;
        }
        else {
            return false;
        }
    },

    IsElement: function (_obj) {
        /// <summary>
        /// 判断目标对象是否为一个DOM元素或包含DOM元素的JQuery对象
        /// </summary>
        /// <param name="_obj"></param>
        /// <returns type=""></returns>
        if (ObjHelper.IsEmpty(_obj)) {
            return false;
        }
        else if (_obj.nodeType) {
            return _obj.nodeType === 1 || _obj.nodeType === 11;
        }
        else if (_obj instanceof jQuery) {
            return _obj.length > 0 && _obj[0].nodeType && _obj[0].nodeType === 1;
        }
        else {
            return false;
        }
    },

    IsFunc: function (_fn) {
        /// <summary>
        /// 判断目标是否为一个函数方法
        /// </summary>
        /// <param name="_fn">目标函数</param>
        /// <returns type=""></returns>
        if (ObjHelper.IsEmpty(_fn)) {
            return false;
        }
        else {
            return typeof _fn === "function";
        }
    },

    DisposeObj: function (_obj) {
        if (!_obj || ObjHelper.IsFunc(_obj)) {
            return;
        }
        else if (Array.isArray(_obj)) {
            var l = _obj.length;
            for (var i = 0; i < l; i++) {
                ObjHelper.DisposeObj(_obj[i]);
                _obj[i] = null;
            }
        }
        else if (typeof _obj === 'object') {
            if (_obj instanceof Controller) {
                _obj.dispose();
            }
            else if (ObjHelper.IsElement(_obj)) {
                $(_obj).remove("", false);
            }
            else {
                for (var i in _obj) {
                    if (_obj.hasOwnProperty(i) && _obj !== _obj[i]) {
                        ObjHelper.DisposeObj(_obj[i]);
                        _obj[i] = undefined;
                    }
                }
            }
        }
    }
}
//#endregion

//#region 布局帮助类方法
var LayoutHelper = {
	Layout2Parent: function (_targetDOM, _mode, _offset, _absolute) {
		/// <summary>
		/// 将目标元素按指定方式定位到相对其父元素的位置
		/// </summary>
		/// <param name="_targetDOM"></param>
		/// <param name="_mode">定位模式[YCenter,XCenter,AllCenter,Cover]</param>
		/// <param name="_offset">偏移[top,bottom,left,right]</param>
		/// <param name="_absolute">是否使用绝对定位(正常情况下没有区别)</param>
	    var tgt = $(_targetDOM);
	    var isAbsolute=false;
		if (ObjHelper.IsEmpty(_absolute) || _absolute === false) {
			tgt.css("position", "relative");
		}
		else {
		    isAbsolute=true;
			tgt.css("position", "absolute");
		}

		var p = $(tgt.parent()[0]);
		var off = $.extend({ top: 0, bottom: 0, left: 0, right: 0 }, _offset);
		var h = tgt.outerHeight(false);
		if (h == 0) {
		    h = tgt[0].scrollHeight;
		}
		var w = tgt.outerWidth(false);
		var ph = p.outerHeight();
		var pw = p.outerWidth();

		var cssTop = isAbsolute ? "top" : "marginTop";
		var cssLeft = isAbsolute ? "left" : "marginLeft";

		switch (_mode) {
			case "YCenter":
				tgt.css(cssTop, ((ph - h) / 2 + off.top - off.bottom) / ph * 100 + "%");
				break;
			case "XCenter":
				tgt.css(cssLeft, ((pw - w) / 2 + off.left - off.right) / pw * 100 + "%");
				break;
		    case "AllCenter":
				tgt.css(cssTop, ((ph - h) / 2 + off.top - off.bottom)+'px');
				tgt.css(cssLeft, ((pw - w) / 2 + off.left - off.right)+'px');
				break;
		    case "Cover":
		        tgt.css('position', 'absolute').css(cssTop, 0).css(cssLeft, 0);
		        tgt.css('height', ph + 'px').css('width', pw + 'px');
		        break;
		}
		p = null;
		off = null;
	},

	PosToBody: function (_refElm, _elm, _pos, _offset) {
		/// <summary>
		/// 将Body中的某个子元素定位到指定元素附近(注意：窗口尺寸改变时需重新定位)
		/// </summary>
		/// <param name="_refElm">定位的参照目标元素</param>
		/// <param name="_elm">要改变位置的元素（必须是body的直接子元素,且是绝对定位）</param>
		/// <param name="方位">九方向定义（right-up,right,right-down,down,down-left(下左对齐),left-down,left,left-up,up,mid）</param>
		/// <param name="_offset">定位坐标偏移(可以是绝对数值，或相对参照目标的百分比)</param>

	    var refpos = _refElm.offset();  //获取参照目标相对body的偏移坐标
		var eh = _elm.innerHeight();
		var ew = _elm.innerWidth();
		var rw = _refElm.innerWidth();
		var rh = _refElm.innerHeight();
		if (!_offset) {
			_offset = { top: 0, left: 0 };
		}
		else {
			if (!_offset['top']) {
				_offset.top = 0;
			}
			else if (_offset.top.toString().slice(-1) === '%') {
				_offset.top = rh * parseFloat(_offset.top) / 100;
			}

			if (!_offset['left']) {
				_offset.left = 0;
			}
			else if (_offset.left.toString().slice(-1) === '%') {
				_offset.left = rw * parseFloat(_offset.left) / 100;
			}

		}
		var dst = {};

		switch (_pos) {
		    case "right-up":
		        dst.top = refpos.top - eh - _offset.top;
		        dst.left = refpos.left + rw + _offset.left;
		        //_elm.offset(dst);
		        break;
		    case "down-left":
		        dst.top = refpos.top + rh + _offset.top;
		        dst.left=refpos.left;
		        //_elm.offset(dst);
		        break;
		    case "cover":
		        dst.top = refpos.top;
		        dst.left = refpos.left;
			default:
				break;
		}
		switch (_elm.css('position')) {
		    case "absolute":
		        _elm.css('top', dst.top + 'px').css('left', dst.left + 'px');
		        break;
		    default:
		        _elm.css('top', dst.top + 'px').css('left', dst.left + 'px');
		        break;
		}
	}
};
//#endregion

//#region 键盘帮助类
function KeypressMonitor() {
	var tgts = {};
	var instance = this;
	instance.Global = {};

	function onPress(ev) {
		var $elm = $(ev.target);
		var id = $elm.attr('id');
		var char = '';
		switch (ev.keyCode) {
			case 8:
				char = 'backspace';
				break;
			case 9:
				char = 'tab';
				break;
			case 13:
				char = 'enter';
				break;
			case 16:
				char = 'shift';
				break;
			case 17:
				char = 'ctrl';
				break;
			case 18:
				char = 'alt';
				break;
			case 20:
				char = 'capslock';
				break;
			case 32:
				char = 'space';
				break;
			case 186:
				if (ev.shiftKey) {
					char = ':'
				}
				else {
					char = ';';
				}
				break;
			case 188:
				if (ev.shiftKey) {
					char = '<';
				}
				else {
					char = ',';
				}
				break;
			case 190:
				if (ev.shiftKey) {
					char = '>'
				}
				else {
					char = '.';
				}
				break;
			default:
				char = String.fromCharCode(ev.keyCode);
				break;
		}
	    //先看看有没有全局事件绑定
		if (instance.Global[char] && instance.Global[char].length > 0) {
		    instance.Global[char][0].call(this, ev);
		}

	    //再看看有没有绑定特定元素
		if (tgts[id]) {
		    if (char != '') {
		        tgts[id].handed = true;
		        notifyKeyEvent(char, id, ev);
		    }
		    else {
		        tgts[id].handed = false;
		    }
		}
	};

	function onDown(ev) {
	    var $elm = $(ev.target);
		var id = $elm.attr('id');
		var char = '';
		switch (ev.keyCode) {
			case 8:
				char = 'backspace';
				break;
		    case 9:
		        char = 'tab';
		        break;
			case 13:
				if (ev.ctrlKey) {
					char = 'ctrl+enter';
				}
				break;
			case 40:
				char = 'down';
				break;
			case 38:
				char = 'up';
				break;
			case 27:
				char = 'esc';
				break;
		}

        //先看看有没有全局事件绑定
		if (instance.Global[char] && instance.Global[char].length > 0) {
		    instance.Global[char][0].call(this, ev);
		}

	    //再看看有没有绑定特定元素
		if (tgts[id]) {
		    if (char != '') {
		        tgts[id].handed = true;
		        notifyKeyEvent(char, id, ev);
		    }
		    else {
		        tgts[id].handed = false;
		    }
		}
	}

	function onInput(ev) {
		var $elm = $(ev.target);
		var id = $elm.attr('id');
		var char = '';
		var v = $elm.val();
		char = v.slice(v.length - 1, v.length);
		switch (char) {
			case "。":
				char = '.';
				break;
			case "，":
				char = ',';
				break;
			case "：":
				char = ":";
				break;
		}
	    //先看看有没有全局事件绑定
		if (instance.Global[char]&&instance.Global[char].length>0) {
		    instance.Global[char][0].call(this, ev);
		}

	    //再看看有没有绑定特定元素
		if (tgts[id]) {
		    if (char != '') {
		        tgts[id].handed = true;
		        notifyKeyEvent(char, id, ev);
		    }
		    else {
		        tgts[id].handed = false;
		    }
		}
	}

	function notifyKeyEvent(char, id, ev) {
	    ev.Char = char;
		//首先看是否针对特定字符绑定了处理函数
		var hs = tgts[id][char];
		if (hs) {
			for (var h in hs) {
				hs[h](ev);
				if (ev.isPropagationStopped()) {
					break;
				}
			}
		}
		else {
			//如果没有，看是否针对任意按键都绑定了处理事件
			hs = tgts[id]['*'];
			if (hs) {
				for (var h in hs) {
					hs[h](ev);
					if (ev.isPropagationStopped()) {
						break;
					}
				}
			}
		}
	}

	$(window).keypress(null, onPress).keydown(null, onDown).on('input', onInput);

	this.on = function (_elm, _char, _fn) {
		/// <summary>
		/// 将指定方法绑定到目标Dom元素的特殊键盘事件
		/// </summary>
		/// <param name="_elm">要监视其键盘事件的元素</param>
		/// <param name="_char">要监视的特定输入值【enter,esc,space,dot,slash,backslash,backspace,colon】</param>
	    /// <param name="_fn">要回调的方法</param>
		var id = _elm.ID();
		var char = _char.toString().trim();
		if (!id) {
			id = 'id' + Math.random();
			_elm.ID(id);
		}

		if (!tgts[id]) {
			tgts[id] = {};
			tgts[id][char] = [];
		}
		else if (!tgts[id][char]) {
			tgts[id][char] = [];
		}
		tgts[id][char].push(_fn);
		return instance;
	};

	this.off = function (_elm, _char, _fn) {
		/// <summary>
		/// 解除特定的或所有的事件绑定
		/// </summary>
		/// <param name="_elm"></param>
		/// <param name="_char"></param>
		/// <param name="_fn">指定要解除的方法，如果为空，则解除所有的事件绑定</param>
		var id = _elm.ID();
		if (ObjHelper.IsEmpty(_char)) {
			if (tgts.hasOwnProperty(id)) {
				ObjHelper.DisposeObj(tgts[id]);
				tgts[id] = null;
			}
		}
		else {
			//TODO：解除对特定键盘输入值的监控
		}
		return instance;
	};

	this.globalOn = function (_char, _fn) {
	    /// <summary>
	    /// 绑定全局特定按键（只有最后注册的方法会得到键盘消息，除非此方法被globalOff调用移除）
	    /// </summary>
	    /// <param name="_char"></param>
	    /// <param name="_fn"></param>
	    /// <returns type=""></returns>
	    if (!ObjHelper.IsFunc(_fn)) {
	        return this;
	    }
	    if (instance.Global.hasOwnProperty(_char)) {
	        var chs = instance.Global[_char];
	        for (var i = 0; i < chs.length; i++) {
	            if (chs[i] === _fn) {
	                chs.swap(0, i);
	                return this;
	            }
	        }
	        chs.push(_fn);
	        chs.swap(0, chs.length - 1);
	        return this;
	    }
	    else {
	        instance.Global[_char] = [];
	        instance.Global[_char].push(_fn);
	        return this;
	    }
	};

	this.globalOff = function (_char, _fn) {
		/// <summary>
		/// 解除绑定的全局按键事件
		/// </summary>
		/// <param name="_char"></param>
		/// <param name="_fn"></param>
	    /// <returns type=""></returns>
	    if (!ObjHelper.IsFunc(_fn)) {
	        return this;
	    }
	    if (instance.Global.hasOwnProperty(_char)) {
	        var hs = instance.Global[_char];
	        for (var i = 0;i < hs.length; i++) {
	            if (hs[i] == _fn) {
	                hs.splice(i, 1);
	                return this;
	            }
	        }
	    }
	    return this;
	};

	this.Dispose = function () {
		$(window).off('keypress', onPress).off('keydown', onDown).off('input', onInput);
		ObjHelper.DisposeObj(instance);
		instance = null;
	};

	return instance;
}

var KeybordHelper = new KeypressMonitor();
//#endregion

//#region Array对象操作方法扩展
Array.prototype.swap = function (_aIndex, _bIndex) {
    /// <summary>
    /// 交换数组中两个元素的位置,将_aIndex位置的元素替换为_bIndex位置的元素
    /// </summary>
    /// <param name="_aIndex" type="Number"></param>
    /// <param name="_bIndex" type="Number"></param>
    var tmp = this.splice(_bIndex, 1)[0];
    this.splice(_aIndex, 1, tmp);
    return this;
};

Array.prototype.eq = function (_index) {
    return this[_index];
};
//#endregion

//#region String扩展方法
String.prototype.ToJSON = function () {
    /// <summary>
    /// 将JSON格式的字符串转为标准的JSON对象
    /// </summary>
    /// <returns type=""></returns>
    if (this.length > 2) {
        return JSON.parse(this.replace(/&quot;/g, '\"').replace(/&gt;/g, '>').replace(/&lt;/g, '<'));
    }
    else {
        return null;
    }
};

String.prototype.NoHtml = function () {
    /// <summary>
    /// 去除字符串中的Html标记
    /// </summary>
    /// <returns type=""></returns>
    return this.replace(/<\/?[^>]*>/g, '').replace(/[ | ]*\n/g, '\n').replace(/&nbsp;/ig, '').trim();
};

String.prototype.MD5 = function () {
    return $.md5(this);
};

//#endregion

//#region 时间对象的扩展方法
Date.prototype.Betten = function (_date) {
	/// <summary>
	/// 计算两个时间间的间隔
	/// </summary>
	/// <param name="_date" type="Date">比较的时间对象</param>
	/// <returns type="Plain Object">两个时间之间相差的年、月、天、小时、分钟数</returns>
    var diff = {
        Years:null,
        Months:null,
        Days: null,
        Hours: null,
        Minutes: null
    };

    var tt = this.getTime() - _date.getTime();
    diff.Minutes = Math.ceil(tt / 60000);
    diff.Hours = Math.floor(tt / 3600000);
    diff.Years = this.getYear() - _date.getYear();

    if (diff.Years === 0) {
        //如果是同年，计算月份差只要
        diff.Months = this.getMonth() - _date.getMonth();
    }
    else {
        //否则只能近似以每个月30天，按小时数除出来
        diff.Months = Math.floor(diff.Hours /(24*30));
    }

    if (diff.Years === 0&&diff.Months === 0) {
        //如果是同年同月，计算天数差值要
        diff.Days = this.getDate() - _date.getDate();
    }
    else {
        diff.Days =Math.floor(diff.Hours / 24);
    }
    return diff;
}

//#endregion

//#region Cookie操作方法
jQuery.Cookie = function (name, value, options) {
	/// <summary>
	/// Cookie管理相关方法
	/// </summary>
	/// <param name="name"></param>
	/// <param name="value"></param>
	/// <param name="options"></param>
	/// <returns type=""></returns>
	if (typeof value != 'undefined') { // name and value given, set cookie  
		options = options || {path:""};
		if (value === null) {
			value = '';
			options.expires = -1;
		}
		value =JSON.stringify(value);
		var expires = '';
		if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString())) {
			var date;
			if (typeof options.expires == 'number') {
				date = new Date();
				date.setTime(date.getTime() + options.expires*60*1000);
			} else {
				date = options.expires;
			}
			expires = '; expires=' + date.toUTCString();
		}

		var path = options.path ? '; path=' + (options.path) : '';
		var domain = options.domain ? '; domain=' + (options.domain) : '';
		var secure = options.secure ? '; secure' : '';
		document.cookie = [name, '=', escape(value), expires, path].join('');
	}
	else {
		var cookieValue = null;
		if (document.cookie && document.cookie != '') {
			var cookies = document.cookie.split(';');
			for (var i = 0; i < cookies.length; i++) {
				var cookie = jQuery.trim(cookies[i]);
				if (cookie.substring(0, name.length + 1) == (name + '=')) {
					cookieValue = cookie.substring(name.length + 1);
					break;
				}
			}
		}
	    try{
	       return JSON.parse(unescape(cookieValue));
	    }
	    catch(ex){
	        return unescape(cookieValue);
	    }
	}
};
//#endregion

//#region 用数组模拟的双向队列
function Queue() {
    var elms = [];

    this.Append = function (obj) {
    	/// <summary>
    	/// 向队列尾部追加一个元素
    	/// </summary>
    	/// <param name="obj"></param>
        /// <returns type="Queue"></returns>
        elms.push(obj);
        return this;
    };

    this.Prepend = function (obj) {
    	/// <summary>
    	/// 向队列头部插入一个元素
    	/// </summary>
        /// <param name="obj"></param>
        /// <returns type="Queue"></returns>
        var tmp = [obj];
        elms = tmp.concat(elms);
        return this;
    };

    this.Dequeue = function () {
    	/// <summary>
    	/// 移除并返回队列头部的元素
        /// </summary>
        if (elms.length > 0) {
            return elms.shift();
        }
        else {
            return null;
        }
    };

    this.Length = function () {
        return elms.length;
    };
    return this;
}
//#endregion

//#region 替换标准的弹出式交互功能
//window.alert = function (_cnt) {
//    console.log(_cnt);
//};
//window.confirm = function () {

//};
//window.prompt = function () {

//};
//#endregion

//#region MD5加密函数

/**
 * jQuery MD5 hash algorithm function
 * 
 * 	<code>
 * 		Calculate the md5 hash of a String 
 * 		String $.md5 ( String str )
 * 	</code>
 * 
 * Calculates the MD5 hash of str using the 禄 RSA Data Security, Inc. MD5 Message-Digest Algorithm, and returns that hash. 
 * MD5 (Message-Digest algorithm 5) is a widely-used cryptographic hash function with a 128-bit hash value. MD5 has been employed in a wide variety of security applications, and is also commonly used to check the integrity of data. The generated hash is also non-reversable. Data cannot be retrieved from the message digest, the digest uniquely identifies the data.
 * MD5 was developed by Professor Ronald L. Rivest in 1994. Its 128 bit (16 byte) message digest makes it a faster implementation than SHA-1.
 * This script is used to process a variable length message into a fixed-length output of 128 bits using the MD5 algorithm. It is fully compatible with UTF-8 encoding. It is very useful when u want to transfer encrypted passwords over the internet. If you plan using UTF-8 encoding in your project don't forget to set the page encoding to UTF-8 (Content-Type meta tag). 
 * This function orginally get from the WebToolkit and rewrite for using as the jQuery plugin.
 * 
 * Example
 * 	Code
 * 		<code>
 * 			$.md5("I'm Persian."); 
 * 		</code>
 * 	Result
 * 		<code>
 * 			"b8c901d0f02223f9761016cfff9d68df"
 * 		</code>
 * 
 * @alias Muhammad Hussein Fattahizadeh < muhammad [AT] semnanweb [DOT] com >
 * @link http://www.semnanweb.com/jquery-plugin/md5.html
 * @see http://www.webtoolkit.info/
 * @license http://www.gnu.org/licenses/gpl.html [GNU General Public License]
 * @param {jQuery} {md5:function(string))
 * @return string
 */

(function ($) {

	var rotateLeft = function (lValue, iShiftBits) {
		return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
	}

	var addUnsigned = function (lX, lY) {
		var lX4, lY4, lX8, lY8, lResult;
		lX8 = (lX & 0x80000000);
		lY8 = (lY & 0x80000000);
		lX4 = (lX & 0x40000000);
		lY4 = (lY & 0x40000000);
		lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
		if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
		if (lX4 | lY4) {
			if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
			else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
		} else {
			return (lResult ^ lX8 ^ lY8);
		}
	}

	var F = function (x, y, z) {
		return (x & y) | ((~x) & z);
	}

	var G = function (x, y, z) {
		return (x & z) | (y & (~z));
	}

	var H = function (x, y, z) {
		return (x ^ y ^ z);
	}

	var I = function (x, y, z) {
		return (y ^ (x | (~z)));
	}

	var FF = function (a, b, c, d, x, s, ac) {
		a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac));
		return addUnsigned(rotateLeft(a, s), b);
	};

	var GG = function (a, b, c, d, x, s, ac) {
		a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac));
		return addUnsigned(rotateLeft(a, s), b);
	};

	var HH = function (a, b, c, d, x, s, ac) {
		a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac));
		return addUnsigned(rotateLeft(a, s), b);
	};

	var II = function (a, b, c, d, x, s, ac) {
		a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac));
		return addUnsigned(rotateLeft(a, s), b);
	};

	var convertToWordArray = function (string) {
		var lWordCount;
		var lMessageLength = string.length;
		var lNumberOfWordsTempOne = lMessageLength + 8;
		var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64;
		var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16;
		var lWordArray = Array(lNumberOfWords - 1);
		var lBytePosition = 0;
		var lByteCount = 0;
		while (lByteCount < lMessageLength) {
			lWordCount = (lByteCount - (lByteCount % 4)) / 4;
			lBytePosition = (lByteCount % 4) * 8;
			lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
			lByteCount++;
		}
		lWordCount = (lByteCount - (lByteCount % 4)) / 4;
		lBytePosition = (lByteCount % 4) * 8;
		lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
		lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
		lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
		return lWordArray;
	};

	var wordToHex = function (lValue) {
		var WordToHexValue = "", WordToHexValueTemp = "", lByte, lCount;
		for (lCount = 0; lCount <= 3; lCount++) {
			lByte = (lValue >>> (lCount * 8)) & 255;
			WordToHexValueTemp = "0" + lByte.toString(16);
			WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2);
		}
		return WordToHexValue;
	};

	var uTF8Encode = function (string) {
		string = string.replace(/\x0d\x0a/g, "\x0a");
		var output = "";
		for (var n = 0; n < string.length; n++) {
			var c = string.charCodeAt(n);
			if (c < 128) {
				output += String.fromCharCode(c);
			} else if ((c > 127) && (c < 2048)) {
				output += String.fromCharCode((c >> 6) | 192);
				output += String.fromCharCode((c & 63) | 128);
			} else {
				output += String.fromCharCode((c >> 12) | 224);
				output += String.fromCharCode(((c >> 6) & 63) | 128);
				output += String.fromCharCode((c & 63) | 128);
			}
		}
		return output;
	};

	$.extend({
		md5: function (string) {
			var x = Array();
			var k, AA, BB, CC, DD, a, b, c, d;
			var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
			var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
			var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
			var S41 = 6, S42 = 10, S43 = 15, S44 = 21;
			string = uTF8Encode(string);
			x = convertToWordArray(string);
			a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
			for (k = 0; k < x.length; k += 16) {
				AA = a; BB = b; CC = c; DD = d;
				a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
				d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
				c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
				b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
				a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
				d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
				c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
				b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
				a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
				d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
				c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
				b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
				a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
				d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
				c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
				b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
				a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
				d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
				c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
				b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
				a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
				d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
				c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
				b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
				a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
				d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
				c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
				b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
				a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
				d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
				c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
				b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
				a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
				d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
				c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
				b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
				a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
				d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
				c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
				b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
				a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
				d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
				c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
				b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
				a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
				d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
				c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
				b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
				a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
				d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
				c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
				b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
				a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
				d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
				c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
				b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
				a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
				d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
				c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
				b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
				a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
				d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
				c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
				b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
				a = addUnsigned(a, AA);
				b = addUnsigned(b, BB);
				c = addUnsigned(c, CC);
				d = addUnsigned(d, DD);
			}
			var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
			return tempValue.toLowerCase();
		}
	});
})(jQuery);

//#endregion